import java.awt.AWTException;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.io.IOException;
import java.net.UnknownHostException;
import com.tinkerforge.AlreadyConnectedException;
import com.tinkerforge.BrickletJoystick;
import com.tinkerforge.BrickletJoystick.PressedListener;
import com.tinkerforge.IPConnection;
import com.tinkerforge.NotConnectedException;
import com.tinkerforge.TimeoutException;

/**
 * Component which allows you to control your pc with a tinkerforge joystick bricklet. 
 * The joystick has the function of a simple computer mouse with one (left) button.
 * 
 * @author Michael Schaer
 * @author Raphael Haenni
 * 
 */
public class MouseController {
	private int speed, port;
	private String host, joystickUID;
	private BrickletJoystick joystick;
	private final Robot robot;
	private static final short DEBOUNCE_PERIOD_VALUE = 30, CALLBACK_TRESHOLD_TOLERANCE_VALUE = 90;
	
	/**
	 * Creates a new MouseController.
	 * @param speed The speed of the mouse cursor.
	 * @param host The host address of the master brick.
	 * @param port The port address of the master brick.
	 * @param joystickUID The UID of the joystick bricklet.
	 */
	public MouseController(final int speed, String host, int port, String joystickUID) throws UnknownHostException, AlreadyConnectedException, IOException, TimeoutException, NotConnectedException, AWTException {
		this.speed = speed;
		this.host = host;
		this.port = port;
		this.joystickUID = joystickUID;
		this.robot = new Robot();
		setUp();
	}
	
	/**
	 * Creates a new MouseController.
	 * @param host The host address of the master brick.
	 * @param port The port address of the master brick.
	 * @param joystickUID The UID of the joystick bricklet.
	 */
	public MouseController(String host, int port, String joystickUID) throws AWTException, UnknownHostException, AlreadyConnectedException, TimeoutException, NotConnectedException, IOException {
		this.speed = 10;
		this.host = host;
		this.port = port;
		this.joystickUID = joystickUID;
		this.robot = new Robot();
		setUp();
	}
	
	// Sets up the connection and the joystick. 
	// Adds the listeners.
	private void setUp() throws AWTException, UnknownHostException, AlreadyConnectedException, IOException, TimeoutException, NotConnectedException {
		IPConnection ipc = new IPConnection();
		ipc.connect(this.host, this.port);
		this.joystick = new BrickletJoystick(this.joystickUID, ipc);
		// The period, in which the listeners are triggered (in ms).
        joystick.setDebouncePeriod(MouseController.DEBOUNCE_PERIOD_VALUE);
        // Configure threshold for "x or y" value outside of [-90..90]"
        joystick.setPositionCallbackThreshold('o', (short)-MouseController.CALLBACK_TRESHOLD_TOLERANCE_VALUE, (short)MouseController.CALLBACK_TRESHOLD_TOLERANCE_VALUE, (short)-MouseController.CALLBACK_TRESHOLD_TOLERANCE_VALUE, (short)MouseController.CALLBACK_TRESHOLD_TOLERANCE_VALUE);
        addPositionReachedListener();
        addPressedListener();
	}
	
	private void addPositionReachedListener() {
			joystick.addPositionReachedListener(new BrickletJoystick.PositionReachedListener() {
            
			public void positionReached(short x, short y) {
            	PointerInfo pointerInfo = MouseInfo.getPointerInfo();
            	Point b = pointerInfo.getLocation();
            	MouseController.this.robot.mouseMove((int)(b.getX()+MouseController.this.speed*(x/100)),(int)(b.getY()-MouseController.this.speed*(y/100)));
            }
        });
	}
	
	private void addPressedListener() {
			this.joystick.addPressedListener(new PressedListener() {
			
			@Override
			public void pressed() {
				MouseController.this.robot.mousePress(InputEvent.BUTTON1_MASK);	
				MouseController.this.robot.mouseRelease(InputEvent.BUTTON1_MASK);
			}
		});	
	}
}
